package org.framework.lazy.cloud.network.heartbeat.server.standalone.application.impl;

import org.framework.lazy.cloud.network.heartbeat.common.NettyClientVisitorContext;
import org.framework.lazy.cloud.network.heartbeat.server.netty.socket.NettyVisitorSocket;
import org.framework.lazy.cloud.network.heartbeat.server.properties.ServerNodeProperties;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.LazyInternalNetworkPenetrationMappingApplication;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.assembler.InternalNetworkPenetrationMappingDTOAssembler;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.internal.network.penetration.mapping.*;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.internal.network.penetration.mapping.LazyInternalNetworkPenetrationMapping;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.domain.model.internal.network.penetration.mapping.LazyInternalNetworkPenetrationMappingRepository;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.command.internal.network.penetration.mapping.*;
import org.framework.lazy.cloud.network.heartbeat.server.standalone.application.dto.LazyInternalNetworkPenetrationMappingDTO;
import org.springframework.transaction.annotation.Transactional;
import org.wu.framework.core.utils.ObjectUtils;
import org.wu.framework.database.lazy.web.plus.stereotype.LazyApplication;
import org.wu.framework.lazy.orm.database.lambda.domain.LazyPage;
import org.wu.framework.web.response.Result;
import org.wu.framework.web.response.ResultFactory;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.wu.framework.lazy.orm.core.persistence.reverse.lazy.ddd.DefaultDDDLazyApplicationImpl;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelFlowAdapter;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * describe 内网穿透映射
 *
 * @author Jia wei Wu
 * @date 2023/12/29 05:21 下午
 * @see DefaultDDDLazyApplicationImpl
 **/
@Slf4j
@LazyApplication
public class LazyInternalNetworkPenetrationMappingApplicationImpl implements LazyInternalNetworkPenetrationMappingApplication {

    @Resource
    LazyInternalNetworkPenetrationMappingRepository lazyInternalNetworkPenetrationMappingRepository;

    @Resource
    ChannelFlowAdapter channelFlowAdapter;

    @Resource
    ServerNodeProperties serverNodeProperties;


    /**
     * describe 新增内网穿透映射
     *
     * @param lazyInternalNetworkPenetrationMappingStoryCommand 新增内网穿透映射
     * @return {@link Result<  LazyInternalNetworkPenetrationMapping  >} 内网穿透映射新增后领域对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Override
    public Result<LazyInternalNetworkPenetrationMapping> story(LazyInternalNetworkPenetrationMappingStoryCommand lazyInternalNetworkPenetrationMappingStoryCommand) {
        LazyInternalNetworkPenetrationMapping lazyInternalNetworkPenetrationMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkPenetrationMappingStoryCommand);
        lazyInternalNetworkPenetrationMapping.setIsDeleted(false);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkPenetrationMapping.setServerId(serverId);

        String clientId = lazyInternalNetworkPenetrationMapping.getClientId();
        String clientTargetIp = lazyInternalNetworkPenetrationMapping.getClientTargetIp();
        Integer clientTargetPort = lazyInternalNetworkPenetrationMapping.getClientTargetPort();
        Integer visitorPort = lazyInternalNetworkPenetrationMapping.getVisitorPort();


        this.changeSocket(clientId, clientTargetIp, clientTargetPort, visitorPort);
        return lazyInternalNetworkPenetrationMappingRepository.story(lazyInternalNetworkPenetrationMapping);
    }

    /**
     * describe 批量新增内网穿透映射
     *
     * @param lazyInternalNetworkPenetrationMappingStoryCommandList 批量新增内网穿透映射
     * @return {@link Result<List< LazyInternalNetworkPenetrationMapping >>} 内网穿透映射新增后领域对象集合
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Transactional
    @Override
    public Result<List<LazyInternalNetworkPenetrationMapping>> batchStory(List<LazyInternalNetworkPenetrationMappingStoryCommand> lazyInternalNetworkPenetrationMappingStoryCommandList) {
        List<LazyInternalNetworkPenetrationMapping> lazyInternalNetworkPenetrationMappingList =
                lazyInternalNetworkPenetrationMappingStoryCommandList
                        .stream()
                        .map(lazyInternalNetworkPenetrationMappingStoryCommand -> {
                            LazyInternalNetworkPenetrationMapping lazyInternalNetworkPenetrationMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkPenetrationMappingStoryCommand);
                            String serverId = serverNodeProperties.getNodeId();
                            lazyInternalNetworkPenetrationMapping.setServerId(serverId);
                            return lazyInternalNetworkPenetrationMapping;
                        })
                        .collect(Collectors.toList());

        Result<List<LazyInternalNetworkPenetrationMapping>> batchStory = lazyInternalNetworkPenetrationMappingRepository.batchStory(lazyInternalNetworkPenetrationMappingList);
        // 开启端口
        for (LazyInternalNetworkPenetrationMapping lazyInternalNetworkPenetrationMapping : lazyInternalNetworkPenetrationMappingList) {
            String clientId = lazyInternalNetworkPenetrationMapping.getClientId();
            String clientTargetIp = lazyInternalNetworkPenetrationMapping.getClientTargetIp();
            Integer clientTargetPort = lazyInternalNetworkPenetrationMapping.getClientTargetPort();
            Integer visitorPort = lazyInternalNetworkPenetrationMapping.getVisitorPort();
            this.changeSocket(clientId, clientTargetIp, clientTargetPort, visitorPort);
        }
        return batchStory;
    }

    /**
     * describe 更新内网穿透映射
     *
     * @param lazyInternalNetworkPenetrationMappingUpdateCommand 更新内网穿透映射
     * @return {@link Result< LazyInternalNetworkPenetrationMapping >} 内网穿透映射领域对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Transactional
    @Override
    public Result<LazyInternalNetworkPenetrationMapping> updateOne(LazyInternalNetworkPenetrationMappingUpdateCommand lazyInternalNetworkPenetrationMappingUpdateCommand) {
        LazyInternalNetworkPenetrationMapping lazyInternalNetworkPenetrationMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkPenetrationMappingUpdateCommand);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkPenetrationMapping.setServerId(serverId);
        // 删除绑定数据
        Result<LazyInternalNetworkPenetrationMapping> story = lazyInternalNetworkPenetrationMappingRepository.story(lazyInternalNetworkPenetrationMapping);
        String clientId = lazyInternalNetworkPenetrationMapping.getClientId();
        String clientTargetIp = lazyInternalNetworkPenetrationMapping.getClientTargetIp();
        Integer clientTargetPort = lazyInternalNetworkPenetrationMapping.getClientTargetPort();
        Integer visitorPort = lazyInternalNetworkPenetrationMapping.getVisitorPort();
        this.changeSocket(clientId, clientTargetIp, clientTargetPort, visitorPort);
        return story;
    }


    /**
     * 变更 网络穿透
     *
     * @param clientId         客户端ID
     * @param clientTargetIp   客户端目标IP
     * @param clientTargetPort 客户端莫表端口
     * @param visitorPort      访客端口
     */
    private void changeSocket(String clientId, String clientTargetIp, Integer clientTargetPort, Integer visitorPort) {
        // 删除 客户端映射
        this.changeCloseSocket(clientId, visitorPort);
        // 更新 客户端映射
        createVisitor(clientId, clientTargetIp, clientTargetPort, visitorPort);
    }


    /**
     * 删除 通道
     *
     * @param clientId    客户端ID
     * @param visitorPort 访客端口
     */
    private void changeCloseSocket(String clientId, Integer visitorPort) {
        // 删除 客户端映射
        List<NettyVisitorSocket> nettyVisitorSocketList = NettyClientVisitorContext.getVisitorSockets(clientId);
        if (!ObjectUtils.isEmpty(nettyVisitorSocketList)) {
            nettyVisitorSocketList = nettyVisitorSocketList.stream()
                    .filter(nettyVisitorSocket -> nettyVisitorSocket.getVisitorPort() == visitorPort).toList();
            if (!ObjectUtils.isEmpty(nettyVisitorSocketList)) {
                // 关闭端口
                for (NettyVisitorSocket nettyVisitorSocket : nettyVisitorSocketList) {
                    try {
                        nettyVisitorSocket.close();
                    } catch (IOException | InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    /**
     * describe 查询单个内网穿透映射
     *
     * @param lazyInternalNetworkPenetrationMappingQueryOneCommand 查询单个内网穿透映射
     * @return {@link Result<  LazyInternalNetworkPenetrationMappingDTO  >} 内网穿透映射DTO对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/
    @Override
    public Result<LazyInternalNetworkPenetrationMappingDTO> findOne(LazyInternalNetworkPenetrationMappingQueryOneCommand lazyInternalNetworkPenetrationMappingQueryOneCommand) {
        LazyInternalNetworkPenetrationMapping lazyInternalNetworkPenetrationMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkPenetrationMappingQueryOneCommand);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkPenetrationMapping.setServerId(serverId);
        return lazyInternalNetworkPenetrationMappingRepository.findOne(lazyInternalNetworkPenetrationMapping).convert(InternalNetworkPenetrationMappingDTOAssembler.INSTANCE::fromInternalNetworkPenetrationMapping);
    }

    /**
     * describe 查询多个内网穿透映射
     *
     * @param lazyInternalNetworkPenetrationMappingQueryListCommand 查询多个内网穿透映射
     * @return {@link Result<List< LazyInternalNetworkPenetrationMappingDTO >>} 内网穿透映射DTO对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Override
    public Result<List<LazyInternalNetworkPenetrationMappingDTO>> findList(LazyInternalNetworkPenetrationMappingQueryListCommand lazyInternalNetworkPenetrationMappingQueryListCommand) {
        LazyInternalNetworkPenetrationMapping lazyInternalNetworkPenetrationMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkPenetrationMappingQueryListCommand);

        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkPenetrationMapping.setServerId(serverId);
        return lazyInternalNetworkPenetrationMappingRepository.findList(lazyInternalNetworkPenetrationMapping).convert(internalNetworkPenetrationMappings -> internalNetworkPenetrationMappings.stream().map(InternalNetworkPenetrationMappingDTOAssembler.INSTANCE::fromInternalNetworkPenetrationMapping).collect(Collectors.toList()));
    }

    /**
     * describe 分页查询多个内网穿透映射
     *
     * @param lazyInternalNetworkPenetrationMappingQueryListCommand 分页查询多个内网穿透映射
     * @return {@link Result<LazyPage< LazyInternalNetworkPenetrationMappingDTO >>} 分页内网穿透映射DTO对象
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Override
    public Result<LazyPage<LazyInternalNetworkPenetrationMappingDTO>> findPage(int size, int current, LazyInternalNetworkPenetrationMappingQueryListCommand lazyInternalNetworkPenetrationMappingQueryListCommand) {
        LazyInternalNetworkPenetrationMapping lazyInternalNetworkPenetrationMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkPenetrationMappingQueryListCommand);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkPenetrationMapping.setServerId(serverId);
        return lazyInternalNetworkPenetrationMappingRepository.findPage(size, current, lazyInternalNetworkPenetrationMapping).convert(page -> page.convert(InternalNetworkPenetrationMappingDTOAssembler.INSTANCE::fromInternalNetworkPenetrationMapping));
    }

    /**
     * describe 删除内网穿透映射
     *
     * @param lazyInternalNetworkPenetrationMappingRemoveCommand 删除内网穿透映射
     * @return {@link Result< LazyInternalNetworkPenetrationMapping >} 内网穿透映射
     * @author Jia wei Wu
     * @date 2023/12/29 05:21 下午
     **/

    @Transactional
    @Override
    public Result<LazyInternalNetworkPenetrationMapping> remove(LazyInternalNetworkPenetrationMappingRemoveCommand lazyInternalNetworkPenetrationMappingRemoveCommand) {
        LazyInternalNetworkPenetrationMapping lazyInternalNetworkPenetrationMapping = InternalNetworkPenetrationMappingDTOAssembler.INSTANCE.toInternalNetworkPenetrationMapping(lazyInternalNetworkPenetrationMappingRemoveCommand);
        String serverId = serverNodeProperties.getNodeId();
        lazyInternalNetworkPenetrationMapping.setServerId(serverId);
        Result<LazyInternalNetworkPenetrationMapping> remove = lazyInternalNetworkPenetrationMappingRepository.remove(lazyInternalNetworkPenetrationMapping);
        // 删除端口映射
        String clientId = lazyInternalNetworkPenetrationMappingRemoveCommand.getClientId();
        Integer visitorPort = lazyInternalNetworkPenetrationMappingRemoveCommand.getVisitorPort();
        this.changeCloseSocket(clientId, visitorPort);
        return remove;
    }

    /**
     * 创建客户端的访问者
     *
     * @param clientId 客户端ID
     */
    @Override
    public Result<Void> createVisitor(String clientId) {
        // 查询代理信息
        LazyInternalNetworkPenetrationMapping lazyInternalNetworkPenetrationMapping = new LazyInternalNetworkPenetrationMapping();
        lazyInternalNetworkPenetrationMapping.setIsDeleted(false);
        lazyInternalNetworkPenetrationMapping.setClientId(clientId);
        return lazyInternalNetworkPenetrationMappingRepository
                .findList(lazyInternalNetworkPenetrationMapping)
                .applyOther(internalNetworkPenetrationMappings -> {
                    for (LazyInternalNetworkPenetrationMapping networkPenetrationMapping : internalNetworkPenetrationMappings) {
                        Integer visitorPort = networkPenetrationMapping.getVisitorPort();
                        String clientTargetIp = networkPenetrationMapping.getClientTargetIp();
                        Integer clientTargetPort = networkPenetrationMapping.getClientTargetPort();
                        this.createVisitor(clientId, clientTargetIp, clientTargetPort, visitorPort);
                    }
                    return ResultFactory.successOf();
                });
    }

    /**
     * 创建访客
     *
     * @param clientId         客户端ID
     * @param clientTargetIp   客户端目标IP
     * @param clientTargetPort 客户端目标端口
     * @param visitorPort      访客端口
     */
    private void createVisitor(String clientId, String clientTargetIp, Integer clientTargetPort, Integer visitorPort) {
        // 更新 客户端映射
        NettyVisitorSocket nettyVisitorSocket = NettyVisitorSocket.NettyVisitorSocketBuilder
                .builder()
                .builderClientId(clientId)
                .builderClientTargetIp(clientTargetIp)
                .builderClientTargetPort(clientTargetPort)
                .builderVisitorPort(visitorPort)
                .builderChannelFlowAdapter(channelFlowAdapter)
                .build();
        try {
            nettyVisitorSocket.startServer();
        } catch (Exception e) {
            log.error("客户端:{},网络端口:{},开放失败", clientId, visitorPort);
            throw new RuntimeException(e);
        }
    }
}